package com.imooc.seckill.service;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.pojo.SeckillVouchers;
import com.imooc.commons.model.pojo.VoucherOrders;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.seckill.config.RedisLock;
import com.imooc.seckill.mapper.SeckillVouchersMapper;
import com.imooc.seckill.mapper.VoucherOrderMapper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * \* @author: bear
 * \* Date: 2021-07-29-15:39
 * \* describe:
 */
@Service
@Slf4j
public class SeckillService {

	@Resource
	private SeckillVouchersMapper seckillVouchersMapper;
	@Resource
	private VoucherOrderMapper voucherOrderMapper;

	@Value("${service.name.ms-oauth-server}")
	private String oauthServerName;

	@Resource
	private RestTemplate restTemplate;
	@Resource
	private RedisTemplate redisTemplate;
	@Resource
	private RedissonClient redissonClient;

	/**
	 * 添加需要抢购的代金券
	 *
	 * @param seckillVouchers
	 */
	@Transactional(rollbackFor = Exception.class)
	public void addSeckillVouchers(SeckillVouchers seckillVouchers){
		//非空校验
		AssertUtil.isTrue(seckillVouchers.getFkVoucherId() == null,"请选择需要抢购的代金卷");
		AssertUtil.isTrue(seckillVouchers.getAmount() == 0,"请输入抢购总数量");
		Date now = new Date();
		AssertUtil.isNotNull(seckillVouchers.getStartTime(),"请输入开始时间");
		//生产环境下放行，这里是方便测试
		AssertUtil.isNotNull(seckillVouchers.getEndTime(),"请输入结束时间");
		AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()),"结束时间不能早于开始时间");
		AssertUtil.isTrue(seckillVouchers.getStartTime().after(seckillVouchers.getEndTime()),"开始时间不能晚于结束时间");

		// ----------注释原始的 关系型数据库 的流程----------
		// 验证数据库中是否已经存在该券的秒杀活动
		// SeckillVouchers seckillFormDB = seckillVouchersMapper.selectVoucher(seckillVouchers.getFkVoucherId());
		// AssertUtil.isTrue(seckillFormDB !=null ,"改卷已经有了抢购活动");
		// // 插入数据库
		// seckillVouchersMapper.save(seckillVouchers);
		// ----------采用 Redis ----------
		String key = RedisKeyConstant.seckill_vouchers.getKey() + seckillVouchers.getFkVoucherId();
		//验证redis中是否已经存在改卷的秒杀活动
		Map<String,Object> seckillVoucherMaps = redisTemplate.opsForHash().entries(key);
		AssertUtil.isTrue(!seckillVoucherMaps.isEmpty() &&(int) seckillVoucherMaps.get("amount")>0,"改卷已经拥有了抢购活动");

		//将数量同步到redis中
		seckillVouchers.setIsValid(1);
		seckillVouchers.setCreateDate(now);
		seckillVouchers.setUpdateDate(now);
		seckillVoucherMaps = BeanUtil.beanToMap(seckillVouchers);
		redisTemplate.opsForHash().putAll(key,seckillVoucherMaps);

	}

	@Resource
	private RedisLock redisLock;

	public ResultInfo doSeckill(Integer voucherId,String accessToken,String path){

		long time = System.currentTimeMillis() + 10000 ; //设置10秒的超时时间

		// RLock lock1= redissonClient.getLock("php1");
		try{
			boolean isLocked = redisLock.trylock(String.valueOf(time));
			// boolean isLocked = lock1.tryLock(10,TimeUnit.SECONDS);
			if(isLocked){
				log.debug("此时获取锁的线程={}",Thread.currentThread().getId());
				//基本参数校验
				AssertUtil.isTrue(voucherId == null || voucherId<0,"请选择要抢购的代金卷");
				AssertUtil.isNotEmpty(accessToken,"请登录");

				//-------注释掉原先走关系型数据库的流程----------------
				//判断此代金卷是否加入抢购
				SeckillVouchers seckillVouchers = seckillVouchersMapper.selectVoucher(voucherId);
				AssertUtil.isTrue(seckillVouchers == null ,"该代金券并未有抢购活动");

				// 判断是否有效
				AssertUtil.isTrue(seckillVouchers.getIsValid() == 0, "该活动已结束");
				// 判断是否开始、结束
				Date now = new Date();
				AssertUtil.isTrue(now.before(seckillVouchers.getStartTime()), "该抢购还未开始");
				AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()), "该抢购已结束");
				// 判断是否卖完
				AssertUtil.isTrue(seckillVouchers.getAmount() < 1, "该券已经卖完了");

				//获取用户信息
				// String url = oauthServerName + "/user/me?access_token={accessToken}";
				//
				// ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
				// if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
				// 	resultInfo.setPath(path);
				// 	return resultInfo;
				// }
				// //这里的data是个linkedHashmap SignInDinerInfo
				// SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap)resultInfo.getData(),new SignInDinerInfo(),false);

				// 判断登录用户是否已抢到(一个用户针对这次活动只能买一次)
				// VoucherOrders orders = voucherOrderMapper.findDinerOrder(dinerInfo.getId(),seckillVouchers.getId());

				// AssertUtil.isTrue(orders !=null,"改用户已经抢到代金卷，无需再抢");

				//扣库存
				int count = seckillVouchersMapper.stockDecrease(seckillVouchers.getId());
				AssertUtil.isTrue(count == 0, "该券已经卖完了");

				// 下单
				VoucherOrders voucherOrders = new VoucherOrders();
				// voucherOrders.setFkDinerId(dinerInfo.getId());
				voucherOrders.setFkDinerId(2);
				// Redis 中不需要维护外键信息
				//voucherOrders.setFkSeckillId(seckillVouchers.getId());
				voucherOrders.setFkVoucherId(seckillVouchers.getFkVoucherId());
				String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
				voucherOrders.setOrderNo(orderNo);
				voucherOrders.setOrderType(1);
				voucherOrders.setStatus(0);
				count = voucherOrderMapper.save(voucherOrders);
				AssertUtil.isTrue(count == 0, "用户抢购失败");
			}
		}catch (Exception e){
			log.error("获取锁失败={},此刻的线程={}",e.getMessage(),Thread.currentThread().getName());
		}finally {
			redisLock.unlock(String.valueOf(time));
			// lock1.unlock();
		}

		return ResultInfoUtil.buildSuccess(path, "抢购成功");
	}

}
